package edu.neu.cloud_factory.controller;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import edu.neu.cloud_factory.entity.po.*;
import edu.neu.cloud_factory.entity.vo.ScheduleVo;
import edu.neu.cloud_factory.service.*;
import edu.neu.cloud_factory.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * <p>
 * 排产计划 前端控制器
 * </p>
 *
 * @author halozhy
 * @since 2021-07-15
 */
@Slf4j
@RestController
@RequestMapping("/api/schedule")
public class ScheduleController {
    @Autowired
    BidService bidService;

    @Autowired
    UserService userService;

    @Autowired
    ScheduleService scheduleService;

    @Autowired
    DeviceService deviceService;

    @Autowired
    OrderService orderService;

    @GetMapping("/list_by_order_id")
    public List<ScheduleVo> listByOrderId(String orderId, String userId) {
        // 需要先拿到bid_id
        QueryWrapper<Bid> bidQueryWrapper = new QueryWrapper<>();
        bidQueryWrapper.eq("b_order_id", orderId);
        User user = userService.getById(userId);
        bidQueryWrapper.eq("b_factory_id", user.getUFactoryId());
        Bid bid = bidService.getOne(bidQueryWrapper); // 理论上一个用户只能对一个订单投一次标，这里只选一个应该没问题

        QueryWrapper<Schedule> scheduleQueryWrapper = new QueryWrapper<>();
        scheduleQueryWrapper.eq("s_bid_id", bid.getBId());
        List<Schedule> scheduleList = scheduleService.list(scheduleQueryWrapper);

        List<Device> deviceList = deviceService.list();
        Map<Long, Device> deviceMap = new HashMap<>();
        deviceList.forEach(device -> {
            deviceMap.put(device.getDId(), device);
        });

        List<ScheduleVo> scheduleVoList = new ArrayList<>();
        for (Schedule schedule :
                scheduleList) {
            ScheduleVo scheduleVo = new ScheduleVo();
            scheduleVo.setBid_id(schedule.getSBidId());
            scheduleVo.setDevice_id(schedule.getSDeviceId());
            scheduleVo.setId(schedule.getSId());
            scheduleVo.setStart_time(schedule.getSStartTime());
            scheduleVo.setEnd_time(schedule.getSEndTime());
            scheduleVo.setDevice_name(deviceMap.get(schedule.getSDeviceId()).getDName());

            scheduleVoList.add(scheduleVo);
        }
        return scheduleVoList;

    }

    @PostMapping("/add")
    public int add(@RequestBody String CONTENT) {
        JSONObject j = JSONObject.parseObject(CONTENT);
        if (Utils.checkEmpty(j, Arrays.asList("user_id", "order_id", "device_id", "start_time", "end_time"))) {
            Order order = orderService.getById(j.getLong("order_id"));
            if (order == null) {
                return -3;
            }
            Device device = deviceService.getById(j.getLong("device_id"));
            if (device == null) {
                return -4;
            }
            // 开始时间不能晚于结束时间，结束时间或开始时间不能晚于收货截止时间
            LocalDateTime st = Utils.parseToLocalDateTime(j.getString("start_time"));
            LocalDateTime et = Utils.parseToLocalDateTime(j.getString("end_time"));
            if (st.isAfter(et)) {
                return -5; // 开始时间不能晚于结束时间
            }
            LocalDateTime deliEndDateTime = order.getODeliverDate().atTime(0, 0, 0);
            if (st.isAfter(deliEndDateTime) || et.isAfter(deliEndDateTime)) {
                return -6; // 结束时间或开始时间不能晚于收货截止时间
            }
            // 通过 order_id 和 user_id 找到bid
            QueryWrapper<Bid> bidQueryWrapper = new QueryWrapper<>();
            bidQueryWrapper.eq("b_order_id", j.getLong("order_id"));
            User user = userService.getById(j.getLong("user_id"));
            bidQueryWrapper.eq("b_factory_id", user.getUFactoryId());
            Bid bid = bidService.getOne(bidQueryWrapper);

            QueryWrapper<Schedule> scheduleQueryWrapper = new QueryWrapper<>();
            scheduleQueryWrapper.eq("s_device_id", device.getDId());
            List<Schedule> scheduleList = scheduleService.list(scheduleQueryWrapper);
            // 如果传入的设备id已经存在于此设备的排产计划里面，那么需要注意时间冲突
            boolean flag = false;
            for (Schedule schedule :
                    scheduleList) {
                if (schedule.getSDeviceId().equals(device.getDId())) {
                    // 验证时间冲突
                    // ts.s_end_time > 传入的start_time AND ts.s_start_time < 传入的end_time
                    if (schedule.getSEndTime().isAfter(st) && schedule.getSStartTime().isBefore(et)) {
                        // 无法模拟时间流逝，只能再次验证这个排产所对应的订单是不是处在已生产状态
                        // 如果这个订单仍在生产，那么就认为冲突
                        Bid b = bidService.getById(schedule.getSBidId());
                        Order o = orderService.getById(b.getBOrderId());
                        if (o.getOState().equals("已生产")){
                            log.info("发现排产时间冲突：schedule id: {}",schedule.getSId());
                            flag = true;
                            break;
                        }
                    }
                }
            }
            if (flag) {
                return -7; // 排产时段和已有冲突
            }

            Schedule schedule = new Schedule();
            schedule.setSDeviceId(j.getLong("device_id"));
            schedule.setSBidId(bid.getBId());
            schedule.setSStartTime(st);
            schedule.setSEndTime(et);
            scheduleService.save(schedule);

            // 添加排产计划的结束之后，把订单状态改为已生产
            order.setOState("已生产");
            orderService.updateById(order);

            // 设备状态改为 开机
            device.setDState("开机");
            deviceService.updateById(device);
            return 0;
        }
        return -2;
    }

    @PostMapping("/update")
    public int update(@RequestBody String CONTENT) {
        JSONObject j = JSONObject.parseObject(CONTENT);
        if (Utils.checkEmpty(j, Arrays.asList("id", "user_id", "order_id", "device_id", "start_time", "end_time"))) {
            Order order = orderService.getById(j.getLong("order_id"));
            if (order == null) {
                return -3;
            }
            Device device = deviceService.getById(j.getLong("device_id"));
            if (device == null) {
                return -4;
            }
            // 不知为何update的时候element ui的选择器会出问题，修改前和修改后的格式不一致，不好处理
            // 索性直接传时间戳进来
            // 开始时间不能晚于结束时间，结束时间或开始时间不能晚于收货截止时间
            LocalDateTime st = LocalDateTime.ofEpochSecond(j.getLong("start_time") / 1000, 0, ZoneOffset.ofHours(8));
            LocalDateTime et = LocalDateTime.ofEpochSecond(j.getLong("end_time") / 1000, 0, ZoneOffset.ofHours(8));
            if (st.isAfter(et)) {
                return -5; // 开始时间不能晚于结束时间
            }
            LocalDateTime deliEndDateTime = order.getODeliverDate().atTime(0, 0, 0);
            if (st.isAfter(deliEndDateTime) || et.isAfter(deliEndDateTime)) {
                return -6; // 结束时间或开始时间不能晚于收货截止时间
            }
            // 通过 order_id 和 user_id 找到bid
            QueryWrapper<Bid> bidQueryWrapper = new QueryWrapper<>();
            bidQueryWrapper.eq("b_order_id", j.getLong("order_id"));
            User user = userService.getById(j.getLong("user_id"));
            bidQueryWrapper.eq("b_factory_id", user.getUFactoryId());
            Bid bid = bidService.getOne(bidQueryWrapper);

            QueryWrapper<Schedule> scheduleQueryWrapper = new QueryWrapper<>();
            scheduleQueryWrapper.eq("s_device_id", device.getDId());
            List<Schedule> scheduleList = scheduleService.list(scheduleQueryWrapper);
            // 如果传入的设备id已经存在于此设备的排产计划里面，那么需要注意时间冲突
            boolean flag = false;
            for (Schedule schedule :
                    scheduleList) {
                if (schedule.getSDeviceId().equals(device.getDId())
                        && !schedule.getSId().equals(j.getLong("id")) ) {
                    // 验证时间冲突，不过不要验证自己和自己是否冲突，否则只有一条排产计划的时候，就无法更改了
                    // ts.s_end_time > 传入的start_time AND ts.s_start_time < 传入的end_time
                    if (schedule.getSEndTime().isAfter(st) && schedule.getSStartTime().isBefore(et)) {
                        flag = true;
                        break;
                    }
                }
            }
            if (flag) {
                return -7; // 排产时段和已有冲突
            }
            Schedule schedule = scheduleService.getById(j.getLong("id"));
            if (schedule == null) {
                return -8;
            }
            schedule.setSDeviceId(j.getLong("device_id"));
            schedule.setSBidId(bid.getBId());
            if (schedule.getSStartTime().isAfter(LocalDateTime.now())) {
                schedule.setSStartTime(st);
                schedule.setSEndTime(et);
            }
            scheduleService.updateById(schedule);

            return 0;
        }
        return -2;
    }

    @GetMapping("/delete")
    public int delete(long id) {
        Schedule schedule = scheduleService.getById(id);
        scheduleService.removeById(id);

        // 检查这个id所对应的竞标信息，如果没有和这个竞标信息有关的排产计划，需要修改对应的订单状态为 已中标
        Map<String, Object> colMap = new HashMap<>();
        QueryWrapper<Schedule> scheduleQueryWrapper = new QueryWrapper<>();
        scheduleQueryWrapper.eq("s_bid_id", schedule.getSBidId());
        List<Schedule> scheduleList = scheduleService.list(scheduleQueryWrapper);
        if (scheduleList.size() == 0) {
            // 说明这个竞标信息下没有生产计划了
            // 先查出来 bid 的完整信息
            Bid bid = bidService.getById(schedule.getSBidId());
            QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
            orderQueryWrapper.eq("o_id", bid.getBOrderId());
            Order order = orderService.getOne(orderQueryWrapper);
            order.setOState("已中标");
            orderService.updateById(order);
        }
        return 0;
    }
}

